import { Piece, PieceColor, PieceType, toPieceType } from './Piece';
import { Move } from './Move';
import { Game } from './Game';
import { Threat } from './Judge';
import colors from 'colors/safe';

export class Board {
    constructor(game: Game) {
        this.game = game;
        this.board = [];
        this.populateBoard();
    }

    public tryMove(move: Move): boolean {
        if (this.game.rules.validateMove(this, move, this.game.currentPlayer)) {
            const sourcePiece = move.getSourcePiece(this);
            const targetPiece = move.getTargetPiece(this);
            this.movePiece(sourcePiece, targetPiece);
            return true;
        } else {
            return false;
        }
    }

    public printBoard(threats?: Threat[]) {
        console.log("〇〇一二三四五六七八");
        for (let i = 0; i < 10; i++) {
            process.stdout.write(`${Board.toChineseDigit(i)}`);
            for (let j = 0; j < 9; j++) {
                if (threats) {
                    const index = threats.findIndex(it => it.position.row === i && it.position.col === j);
                    if (index >= 0) {
                        process.stdout.write(colors.inverse(this.board[i][j].toPrettyPrint()));
                    } else {
                        process.stdout.write(this.board[i][j].toPrettyPrint());
                    }
                } else {
                    process.stdout.write(this.board[i][j].toPrettyPrint());
                }
            }
            console.log();
        }
    }

    public movePiece(source: Piece, target: Piece) {
        if (target.type === PieceType.EMPTY) {
            const { row, col } = target;
            const temp = this.board[target.row][target.col];
            this.board[target.row][target.col] = this.board[source.row][source.col];
            this.board[source.row][source.col] = temp;
            target.moveTo(source.row, source.col);
            source.moveTo(row, col);
        } else {
            this.board[target.row][target.col] = source;
            this.board[source.row][source.col] = new Piece(PieceType.EMPTY, PieceColor.BLANK, source.row, source.col);
            source.moveTo(target.row, target.col);
        }
    }

    public erasePiece(piece: Piece) {
        this.board[piece.row][piece.col] = new Piece(PieceType.EMPTY, PieceColor.BLANK, piece.row, piece.col);
    }

    public setPiece(piece: Piece) {
        this.board[piece.row][piece.col] = piece;
    }

    public getPieceAt(row: number, col: number) {
        return this.board[row][col];
    }

    public getPiecesOfColor(color: PieceColor): Piece[] {
        return this.board.reduce((a, v) => a.concat(v), []).filter(it => it.color === color);
    }

    public load(board: string) {
        board = board.trim();
        if (board.length % 3 !== 0) {
            throw new Error('Corrupted board loading string: ' + board);
        }
        this.populateBoard();
        for (let i = 0; i < board.length; i += 3) {
            const piece = board.charAt(i);
            const row = Number(board.charAt(i + 1));
            const col = Number(board.charAt(i + 2));
            this.board[row][col] = new Piece(toPieceType(piece), piece < 'a' ? PieceColor.RED : PieceColor.DARK, row, col);
        }
    }

    public save(): string {
        return this.board.map(row => row.map(it => it.type === PieceType.EMPTY ? '' : (`${it.toString()}${it.row}${it.col}`)).join('')).join('');
    }

    public clone(): Board {
        const board = new Board(this.game);
        board.load(this.save());
        return board;
    }

    private populateBoard() {
        for (let i = 0; i < 10; i++) {
            this.board[i] = [];
            for (let j = 0; j < 9; j++) {
                this.board[i][j] = new Piece(PieceType.EMPTY, PieceColor.BLANK, i, j);
            }
        }
    }

    private static toChineseDigit(n: number): string {
        switch (n) {
            case 0: return '〇';
            case 1: return '一';
            case 2: return '二';
            case 3: return '三';
            case 4: return '四';
            case 5: return '五';
            case 6: return '六';
            case 7: return '七';
            case 8: return '八';
            case 9: return '九';
            default: return '';
        }
    }

    private readonly board: Piece[][];
    private readonly game: Game;
}

export const baseBoard = 'r00k01b02a03g04a05b06k07r08c21c27p30p32p34p36p38P60P62P64P66P68C71C77R90K91B92A93G94A95B96K97R98';
